/*
 * scan.c
 *
 *  Created on: 2010-12-28
 *      Author: root
 */

#include "scan.h"

/*
check an valid protocol type
*/
extern int VALID_PROTO(PROTO_TYPE type)
{
int nRet = 0;
switch(type)
{
case UDP_PROTO:
case TCP_PROTO:
         nRet =1;
         break;
default : break;
}
return nRet;
}

/*
get the name of a specific protocol
*/
extern char * STR_PROTO(PROTO_TYPE type)
{
char szRet[20];
switch(type)
{
case UDP_PROTO: sprintf(szRet, "udp");break;
case TCP_PROTO: sprintf(szRet, "tcp"); break;
default: sprintf(szRet,  "UNKNOW PROTO");break;
}
return szRet;
}

/*
define tcp status for a tcp connection
*/
#ifndef _TCP_STATUS_
#define _TCP_STATUS_
#define TCP_STATUS_COUNT 12 /*size of this array*/
const char * TCP_STATUS [] =
{
  "ERROR_STATUS",
  "TCP_ESTABLISHED",
  "TCP_SYN_SENT",
  "TCP_SYN_RECV",
  "TCP_FIN_WAIT1",
  "TCP_FIN_WAIT2",
  "TCP_TIME_WAIT",
  "TCP_CLOSE",
  "TCP_CLOSE_WAIT",
  "TCP_LAST_ACK",
  "TCP_LISTEN",
  "TCP_CLOSING",
};
#endif


int init (struct port * port)
{
        if(!port)
        {
                return 0;
        }
        memset(port->szLocalAddr, '\0', ADDR_SIZE);
        memset(port->szRemotAddr, '\0', ADDR_SIZE);
        port->nLocalPort = port->nRemotPort = INVALID_PORT;
        port->nStatus = ERROR_STATUS;
        port->nVersion = IP_VERSION_ERROR;
        port->proto = ERROR_PROTO;
        return 1;
}

int fill_data (struct port * port, const char * data)
{
        int nID, nLPort, nRPort, nStatus;
        char local_address[128], remote_address[128], other[8192];
        const char* msg = data;
        memset(local_address, 0, 128);
        memset(remote_address, 0, 128);
        sscanf(msg,
                "%d: %[0-9A-Fa-f]:%X %[0-9A-Fa-f]:%X %X %s",
                &nID,
                local_address,
                &nLPort,
                remote_address,
                &nRPort,
                &nStatus,
                other
                );
        //it is an ip address of ipv6,so with format
        //like XXXX.XXXX.XXXX.XXXX.XXXX.XXXX.XXXX.XXXX
        //then when we read this string, its length is
        //larger than 8(which is the length of a ip address
        //of ipv4 like "XX.XX.XX.XX")
        if(strlen(local_address) > 8)
        {
                char * p = port->szLocalAddr;
                char * q = local_address;
                sprintf(p, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
                        "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
                        q[6],  q[7],  q[4],  q[5],
                        q[2],  q[3],  q[0],  q[1],
                        q[14], q[15], q[12], q[13],
                        q[10], q[12], q[8],  q[9],
                        q[23], q[23], q[20], q[21],
                        q[18], q[19], q[16], q[17],
                        q[30], q[31], q[28], q[29],
                        q[26], q[27], q[24], q[25]
                        );

                p = port->szRemotAddr;
                q = remote_address;

                      sprintf(p, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
                                  "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
                                  q[6],  q[7],  q[4],  q[5],
                                  q[2],  q[3],  q[0],  q[1],
                                  q[14], q[15], q[12], q[13],
                                  q[10], q[12], q[8],  q[9],
                                  q[23], q[23], q[20], q[21],
                                  q[18], q[19], q[16], q[17],
                                  q[30], q[31], q[28], q[29],
                                  q[26], q[27], q[24], q[25]
                                  );
                          port->nVersion =IP_VERSION6;
        }//we got an address of ipv4
        else
        {
                char * p = port->szLocalAddr;
                char * q = local_address;
                sprintf(p, "%c%c%c%c%c%c%c%c",
                        q[6],  q[7],  q[4],  q[5],
                        q[2],  q[3],  q[0],  q[1]);

                p = port->szRemotAddr;
                q = remote_address;
                sprintf(p, "%c%c%c%c%c%c%c%c",
                        q[6],  q[7],  q[4],  q[5],
                        q[2],  q[3],  q[0],  q[1]);
                //set the ip version
                port->nVersion = IP_VERSION4;
        }
        port->nLocalPort = nLPort;
        port->nRemotPort = nRPort;
        port->nStatus = nStatus;
        return 1;
}


char* print (const struct port* port, int flag, PROTO_TYPE type)
{

        if(!port || !VALID_PROTO(type))
        {
                fprintf(stderr, "invalid proto type %d\n", type);
                return 0;
        }
        else
        {
                int npos;
                static char szRet[256];

                #ifdef __LINUX__
                struct servent* serv;
                #endif

                const char * l = port->szLocalAddr;
                const char * r = port->szRemotAddr;
                // to avoiding an exception when  index(we used it to get
                //a string from array TCP_STATUS) is larger than the
                //size of array TCP_STATUS, we check 'nStatus'
                memset(szRet, 0, 256);
                npos = (port->nStatus >=0 && port->nStatus < TCP_STATUS_COUNT) ?
                        port->nStatus : 0;
                if(port->nVersion == IP_VERSION6)
                {
                        const char * p = port->szLocalAddr;
                        char szTem[20], szRem[256];
                        int i = 32;
                        sprintf(szRet,  "%d/%s\t\t  %c%c%c%c.%c%c%c%c.%c%c%c%c.%c%c%c%c."
                                "%c%c%c%c.%c%c%c%c.%c%c%c%c.%c%c%c%c",
                                type,
                                STR_PROTO(type),
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i]
                                );
                        memset(szTem, 0, 20);
                        sprintf(szTem, ":%d", port->nLocalPort);
                        strcat(szRet + strlen(szRet), szTem);

                        #ifdef __LINUX__
                         serv = getservbyport(ntohs(port->nLocalPort),
                         STR_PROTO(type));
                         if(serv)
                         {
                            strcat(szRet + strlen(szRet), "(");
                            strcat(szRet + strlen(szRet), serv->s_name);
                            strcat(szRet + strlen(szRet), ") ");
                          }
                        #endif
                        strcat(szRet + strlen(szRet), "\t\t");
                        i = 32;
                        p = port->szRemotAddr;
                        memset(szRem, 0, 256);
                        sprintf(szRem, "%c%c%c%c.%c%c%c%c.%c%c%c%c.%c%c%c%c."
                                "%c%c%c%c.%c%c%c%c.%c%c%c%c.%c%c%c%c",
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i],
                                p[--i], p[--i], p[--i], p[--i]
                                );
                memset(szTem, 0, 20);
                                sprintf(szTem, ":%d\t%s\n", port->nRemotPort, TCP_STATUS[npos]);
                            strcat(szRem + strlen(szRem), szTem);
                                strcat(szRet + strlen(szRet), type == TCP_PROTO ? szRem : " *:*");
                }
                else
                {
                        const char * p = port->szLocalAddr;
                        char szTem[20], szRem[256];
                        int i = 8;

                        sprintf(szRet, "%d/%s\t\t  %c%c.%c%c.%c%c.%c%c",
                                type,
                                STR_PROTO(type),
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i]
                                );
                        memset(szTem, 0, 20);
                        sprintf(szTem, ":%d", port->nLocalPort);
                        strcat(szRet + strlen(szRet), szTem);
                         #ifdef __LINUX__
                         serv = getservbyport(htons(port->nLocalPort),
                         STR_PROTO(type));
                         if(serv)
                         {
                            strcat(szRet + strlen(szRet), "(");
                            strcat(szRet + strlen(szRet), serv->s_name);
                            strcat(szRet + strlen(szRet), ") ");
                          }
                        #endif
                        strcat(szRet + strlen(szRet), "\t\t");
                        i = 8;
                        p = port->szRemotAddr;
                        memset(szRem, 0, 256);
                        sprintf(szRem, "%c%c.%c%c.%c%c.%c%c",
                                p[--i], p[--i],p[--i],p[--i],
                                p[--i], p[--i],p[--i],p[--i]
                                );

                        memset(szTem, 0, 20);
                        sprintf(szTem, ":%d\t%s\n", port->nRemotPort, TCP_STATUS[npos]);
                        strcat(szRem + strlen(szRem), szTem);
                        strcat(szRet + strlen(szRet), type == TCP_PROTO ? szRem : "*:*");
                }
                if(flag)
                {
                        printf("%s\n", szRet);
                }
                return szRet;
        }
}
int get_port_list(struct port * array, int size, PROTO_TYPE type)
{
    char * lines[65535] = {0}, buffer[8192] = {0};
        int len = 0, i = 0, nRet = 0, mid = 0;
        FILE * procfs = 0;
        char * fv4, *fv6;
        if(!array || size <= 0)
        {
                fprintf(stderr, "invalid parameter in get_tcp_port_list()\n");
                return 0;
    }
        if(type == UDP_PROTO)
        {
         fv4 = PROC_UDP;
         fv6 = PROC_UDP6;
        }else
        if(type == TCP_PROTO)
        {
         fv4 = PROC_TCP;
         fv6 =PROC_TCP6;
        }else
        {
                return 0;
        }
        if((procfs = fopen(fv4, "r")) == NULL)
        {
                fprintf(stderr, "File %s open faile\n", fv4);
        }else
        {
                fgets(buffer, sizeof(buffer), procfs);
                while(!feof(procfs))
                {
                        memset(buffer, 0, sizeof(buffer));
                        fgets(buffer, sizeof(buffer), procfs);
                        len = strlen(buffer) + 1;
                        lines[i] = (char*)malloc(len);
                        if(lines[i] != 0)
                        {
                                strcpy(lines[i++], buffer);
                                //printf("%s\n", buffer);
                        }
                }
                fclose(procfs);
        }
        mid = i - 1;
        if((procfs = fopen(fv6, "r")) == NULL)
        {
                //fprintf(stderr, "open file %s fail\n", fv6);
        }else
        {
                fgets(buffer, sizeof(buffer), procfs);
                while(!feof(procfs))
                {
                        memset(buffer, 0, sizeof(buffer));
                        fgets(buffer, sizeof(buffer), procfs);
                        len = strlen(buffer) + 1;
                        lines[i] = (char*)malloc(len);
                        if(lines[i] != 0)
                        {
                                strcpy(lines[i++], buffer);
                                //printf("%s\n", buffer);

                        }
                }
                fclose(procfs);
        }
        i--; i--;
        while(i >= 0 && nRet < size)
        {
                if(i == mid)
                {
                  i = i - 1;
                  free(lines[mid]);
                  continue;
                }
                memset((void*)&(array[nRet]), 0, sizeof(struct port));
                init(array + nRet);
                if(!fill_data(array + nRet, lines[i--]))
                {
                        fprintf(stderr, "line:\n %s \n has error format\n");
                        continue;
                }else
                {
                  //printf("%s\n", lines[i+1]);
                  (array[nRet]).proto = type;
                }
        free(lines[i + 1]);
                ++nRet;
        }
        return nRet;
}

/*int do_command(unsigned int command)
{
  struct port port[1024];
  int size, n = 0;

  if(command == SHOW_TCP)
  {
    n = 0;
    size = get_port_list(port, 1024, TCP_PROTO);
    printf("protocol\t\tlocal_address\t\tremote_address\t\tstatus\n");
    while(n < size)
    {
      char * szRet = print(&(port[n++]), 0, port[n].proto);
      printf("%s\n", szRet);
    }
  }else
  if(command == SHOW_UDP)
  {
    size = get_port_list(port, 1024, UDP_PROTO);
    printf("protocol\t\tlocal_address\t\tremote_address\n");
    while(n < size)
    {
      char * szRet = print(&(port[n++]), 0, port[n].proto);
      printf("%s\n", szRet);
    }

  }else
  if(command == SHOW_ALL)
  {
    size = get_port_list(port, 1024, TCP_PROTO);
    while(n < size)
    {
       /*
       char * szRet = print(&(port[n++]), 0, port[n].proto);
       printf("%s\n", szRet);
       */
       /*for my beloved gril, we used another format of printf*/
/*        print(&(port[n++]), 1, port[n].proto);
  }
    n = 0;
    size = get_port_list(port, 1024, UDP_PROTO);
    while(n < size)
    {
      char * szRet = print(&(port[n++]), 0, port[n].proto);
      printf("%s\n", szRet);
    }

  }else
  {
   fprintf(stderr, "UNKNOW COMMAND %d\n", command);
  }
  return 1;
}*/
